package cn.edu.lcu.cs.javaprogramming.db;

import cn.edu.lcu.cs.javaprogramming.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * <h3>自编的结果集处理器</h3>
 * <p>
 * 将结果集里的数据转置到List中。<br>
 * 通过此例，可感受编程规范的好处。
 * </p>
 * <h3>数据库命名规范：</h3>
 * <ul>
 * <li>表名、列名全部小写，若有多个单词，单词间加下划线。</li>
 * </ul>
 * <h3>Java命名规范（驼峰模式）：</h3>
 * <ul>
 * <li>类型（类、接口、枚举、注解等）名首字母大写，变量、方法名首字母小写。</li>
 * <li>除了首个单词，后续的单词首字母大写，单词间没有分隔符。</li>
 * </ul>
 *
 * @param <T> 处理的JavaBean类型
 * @author Ling
 */
public class ListBeanHandler<T> {
    private Class<? extends T> type;

    public ListBeanHandler(Class<? extends T> type) {
        super();
        this.type = type;
    }

    /**
     * 将结果集里的数据转置到List中
     *
     * @param rs
     * @return
     */
    public List<T> handle(ResultSet rs) {
        try {
            if (rs == null || rs.isClosed()) {
                return null;
            }
            List<T> list = new ArrayList<>();
            // 获取结果集元数据
            ResultSetMetaData metaData = rs.getMetaData();
            while (rs.next()) {
                // 创建一个新实例
                T t = type.getDeclaredConstructor().newInstance();
                // 获取结果集列数
                int columnCount = metaData.getColumnCount();
                // 循环处理每一列，将列值赋值给对象的属性
                for (int i = 1; i <= columnCount; i++) {
                    // 获取列值
                    Object columnValue = rs.getObject(i);
                    // 获取列名（驼峰模式，或下划线模式）
                    // 如直接返回下划线风格的列名，或返回驼峰风格的别名：select student_no, student_name as studentName from student
                    String columnName = metaData.getColumnName(i);
                    // 把数据库编程规范中的下划线风格的列名转换为Java规范中的驼峰模式
                    // 如：map_under_score_to_camel_case --> MapUnderScoreToCamelCase
                    // 如果返回的是驼峰模式的别名，也可正常处理，因为它被拆分为一个独立的单词
                    String camelCaseFieldName = StringUtils.mapUnderScoreToCamelCase(columnName);
                    // 首字母小写，即可变换为符合Java规范的Field名
                    camelCaseFieldName = StringUtils.toLowerCaseFirstLetter(camelCaseFieldName);
                    // 根据Field名称获取Field对象
                    Field field = t.getClass().getDeclaredField(camelCaseFieldName);
                    // 设置Field可访问
                    field.setAccessible(true);
                    // 通过反射，设置对象t的field值
                    field.set(t, columnValue);
                }
                list.add(t);
            }
            return list;
        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
                 | NoSuchMethodException | SecurityException | NoSuchFieldException | SQLException e) {
            e.printStackTrace();
        }
        return null;
    }
}
